package proxyPool

import (
	"gitee.com/kaylee595/gkaylee/kerr"
	"golang.org/x/net/publicsuffix"
	"net/url"
	"strconv"
	"strings"
)

type URLParser interface {
	// Parse 将传入数据解析为 *url.URL. 如果Scheme为空, 将由Generate自动补充.
	Parse(rawURL string) (*url.URL, error)
}

type URLParserFunc func(rawURL string) (*url.URL, error)

func (f URLParserFunc) Parse(rawURL string) (*url.URL, error) {
	return f(rawURL)
}

func verifyPort(protStr string) bool {
	port, err := strconv.ParseUint(protStr, 10, 16)
	return err == nil && port > 0
}

func verifyIP(ipStr string) bool {
	fields := strings.SplitN(ipStr, ".", 4)
	if len(fields) < 4 {
		return false
	}
	for _, field := range fields {
		_, err := strconv.ParseUint(field, 10, 8)
		if err != nil {
			return false
		}
	}
	return true
}

func verifyDomain(domain string) bool {
	suffix, icann := publicsuffix.PublicSuffix(domain)
	return icann && suffix != ""
}

func verifyHost(host string) bool { return verifyIP(host) || verifyDomain(host) }

var (
	// StdURLParser 解析标准格式的URL.
	// 格式: [scheme:][//[userinfo@]host][/]path[?query][#fragment].
	// 中括号的内容为可空内容.
	// EG: socks5://user:pwd@example:1466
	StdURLParser URLParserFunc = func(row string) (*url.URL, error) {
		isPad := !strings.Contains(row, "://")
		if isPad { // 补充协议帮助解析
			row = "http://" + row
		}
		u, err := url.Parse(row)
		if err != nil {
			return nil, kerr.WrapTraceStack(err)
		}
		if isPad { // 如果协议是补充的, 则应该在解析后删除
			u.Scheme = ""
		}
		return u, nil
	}

	// PlatformStdURLParser 解析代理平台搞出来的代理格式.
	// 可以解析以下四种格式:
	// hostname:port:username:password ;
	// username:password:hostname:port ;
	// username:password@hostname:port ;
	// hostname:port@username:password ;
	// 其中第三种格式为标准格式, 如果你明确你的格式是第三种, 直接采用 StdURLParser 解析, 性能会快百纳秒左右!
	PlatformStdURLParser URLParserFunc = func(rawURL string) (*url.URL, error) {
		fields := strings.FieldsFunc(rawURL, func(r rune) bool {
			return r == ':' || r == '@'
		})
		if len(fields) < 4 {
			return nil, kerr.WrapTraceStack(ErrInvalidURL)
		}
		if verifyHost(fields[0]) {
			if !verifyPort(fields[1]) {
				return nil, kerr.WrapTraceStack(ErrInvalidURL)
			}
			return &url.URL{
				Host: fields[0] + ":" + fields[1],
				User: url.UserPassword(fields[2], fields[3]),
			}, nil
		}
		if !verifyHost(fields[2]) || !verifyPort(fields[3]) {
			return nil, kerr.WrapTraceStack(ErrInvalidURL)
		}
		return &url.URL{
			Host: fields[2] + ":" + fields[3],
			User: url.UserPassword(fields[0], fields[1]),
		}, nil
	}
)
